﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Numerics;
using System.Text;
using ParserGenerator.CharacterMapping;
using ParserGenerator.Exceptions;
using ParserGenerator.Extensions;
using ParserGenerator.Input;

namespace ParserGenerator.SyntacticAnalysis
{
    public sealed partial class ProgramDescriptionParser
    {
        #region tokenizer semantic code
        private readonly StringBuilder _sb = new StringBuilder();
        private readonly List<byte> _bsb = new List<byte>();
        private bool _isQuote;
        private SourceContext _context;

        private void ScanIdentifierName()
        {
            string value = TokenContext.GetSource();
            TokenValue = Identifier.New(value, TokenContext);
        }

        private void ScanNullValue()
        {
            TokenValue = NullLiteral.New(TokenContext);
        }

        private void ScanBooleanValue(bool value)
        {
            TokenValue = BooleanLiteral.New(value, TokenContext);
        }

        private void ScanIntegerValue(int radix)
        {
            ScanIntegerValue(radix, false);
        }

        private void ScanIntegerValue(int radix, bool forSlotReference)
        {
            string str = TokenContext.GetSource();
            int start = (radix == 10) ? 0 : 2;
            if (forSlotReference)
                start++;
            BigInteger v = 0;
            for (int i = start; i < str.Length; i++)
            {
                int d = str[i].ValueOfDigit();
                v = v * radix + d;
            }
            TokenValue = IntegerLiteral.New(v, TokenContext);
        }

        private void ScanFloatValue()
        {
            string str = TokenContext.GetSource();
            double value;
            if (double.TryParse(str, out value))
                TokenValue = FloatLiteral.New(value, TokenContext);
            else
                TokenValue = FloatLiteral.New(double.PositiveInfinity, TokenContext);
        }

        private void ScanComplexValue()
        {
            string str = TokenContext.GetSource();
            str = str.Substring(0, str.Length - 1);
            double value;
            if (double.TryParse(str, out value))
                TokenValue = ComplexLiteral.New(new Complex(0.0, value), TokenContext);
            else
                TokenValue = ComplexLiteral.New(new Complex(0.0, double.PositiveInfinity), TokenContext);
        }

        private void ScanInfinityFloatValue()
        {
            TokenValue = FloatLiteral.New(double.PositiveInfinity, TokenContext);
        }

        private void ScanNaNFloatValue()
        {
            TokenValue = FloatLiteral.New(double.NaN, TokenContext);
        }

        private void ScanInfinityComplexValue()
        {
            TokenValue = ComplexLiteral.New(new Complex(0.0, double.PositiveInfinity), TokenContext);
        }

        private void ScanNaNComplexValue()
        {
            TokenValue = ComplexLiteral.New(new Complex(0.0, double.NaN), TokenContext);
        }

        private void BeginString(int start, bool isQuote)
        {
            _sb.Clear();
            _isQuote = isQuote;
            _context = TokenContext;
            PushStart(start);
        }

        private void EndString()
        {
            EndString(false);
        }

        private void EndString(bool asIdentifier)
        {
            string value = _sb.ToString();
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            if (asIdentifier)
                TokenValue = Identifier.New(value, TokenContext);
            else
                TokenValue = StringLiteral.New(value, _isQuote, TokenContext);
            PopStart();
        }

        private void BeginByteString(int start, bool isQuote)
        {
            _bsb.Clear();
            _isQuote = isQuote;
            _context = TokenContext;
            PushStart(start);
        }

        private void EndByteString()
        {
            ReadOnlyCollection<byte> value = _bsb.ToArray().AsReadOnly();
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            TokenValue = ByteStringLiteral.New(value, _isQuote, TokenContext);
            PopStart();
        }

        private SyntaxErrorException UnterminatedStringLiteral()
        {
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            return SyntaxError(SR.UnterminatedStringLiteral, TokenContext);
        }

        private SyntaxErrorException UnterminatedVerbatimIdentifier()
        {
            TokenContext = new SourceContext(_context.Begin, TokenContext.End);
            return SyntaxError(SR.UnterminatedVerbatimIdentifier, TokenContext);
        }

        private void AppendChar(char value)
        {
            _sb.Append(value);
        }

        private void AppendByte(char value)
        {
            _bsb.Add((byte)value);
        }

        private void AppendString(string value)
        {
            _sb.Append(value);
        }

        private void AppendTokenString()
        {
            string str = TokenContext.GetSource();
            _sb.Append(str);
        }

        private void AppendTokenStringAsBytes()
        {
            string str = TokenContext.GetSource();
            foreach (int c in str.EnumCodepoints())
                _bsb.Add((byte)c);
        }

        private void AppendEscape(int n, bool isString, bool isOctal)
        {
            string str = TokenContext.GetSource();
            int v = 0;
            if (isOctal)
            {
                for (int i = 0; i < n; i++)
                {
                    int ch = str[1 + i];
                    int d = ch.ValueOfDigit();
                    v = (v << 3) | d;
                }
            }
            else
            {
                for (int i = 0; i < n; i++)
                {
                    int ch = str[2 + i];
                    int d = ch.ValueOfDigit();
                    v = (v << 4) | d;
                }
            }
            if (isString)
            {
                if (v < CharExtensions.MinChar || v > CharExtensions.MaxChar)
                {
                    string message = string.Format(SR.CharMustBeInRange, CharInterval.Universal);
                    throw SyntaxError(message);
                }
                _sb.Append(char.ConvertFromUtf32(v));
            }
            else
            {
                _bsb.Add((byte)(v & 0xFF));
            }
        }
        #endregion

        #region parser semantic code
        //private readonly ExpressionValidator _validator = new ExpressionValidator();

        //private string GetGrammarFragmentName(SourceContext context)
        //{
        //    if (context == null)
        //        throw new ArgumentNullException(nameof(context));
        //    string name = context.Document.Name;
        //    while (name.IndexOf('.') != -1)
        //        name = Path.GetFileNameWithoutExtension(name);
        //    if (name.IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
        //        return "Grammar";
        //    else
        //        return name.ToPascal();
        //}

        //private void CheckCharRange(CharLiteral first, CharLiteral last, SourceContext context)
        //{
        //    if (first == null)
        //        throw new ArgumentNullException(nameof(first));
        //    if (last == null)
        //        throw new ArgumentNullException(nameof(last));
        //    if (first.Value > last.Value)
        //    {
        //        SyntaxErrorException e = new SyntaxErrorException(SR.FirstLastNotRange);
        //        e.AddLocation(context);
        //        throw e;
        //    }
        //}

        //private StringLiteral ConcatStringLiterals(Sequence<StringLiteral> list, SourceContext context)
        //{
        //    if (list == null)
        //        throw new ArgumentNullException(nameof(list));
        //    StringBuilder sb = new StringBuilder();
        //    foreach (StringLiteral item in list)
        //        sb.Append(item.Value);
        //    string value = sb.ToString();
        //    return StringLiteral.New(value, context);
        //}

        //private ByteStringLiteral ConcatByteStringLiterals(Sequence<ByteStringLiteral> list, SourceContext context)
        //{
        //    if (list == null)
        //        throw new ArgumentNullException(nameof(list));
        //    List<byte> bsb = new List<byte>();
        //    foreach (ByteStringLiteral item in list)
        //        bsb.AddRange(item.Value);
        //    byte[] value = bsb.ToArray();
        //    return ByteStringLiteral.New(value, context);
        //}

        //private Statement CreateAssignStatement(Sequence<Expression> targets, Expression value, SourceContext context)
        //{
        //    _validator.SetUsage(targets, ExpressionUsage.Store);
        //    return Statement.NewAssign(targets, value, context);
        //}

        //private Statement CreateAugmentedAssignStatement(Expression target, BinaryOp op, Expression value, SourceContext context)
        //{
        //    _validator.SetUsage(target, ExpressionUsage.Store);
        //    switch (target.Tag)
        //    {
        //        case Expression.Category.Access:
        //        case Expression.Category.Subscript:
        //        case Expression.Category.NameReference:
        //            break;
        //        default:
        //            SyntaxErrorException e = new SyntaxErrorException(SR.IllegalExprForAugAssign);
        //            e.AddLocation(target.Context);
        //            throw e;
        //    }
        //    return Statement.NewAugAssign(target, op, value, context);
        //}

        //private Sequence<Statement> ExpandElseIfList(Sequence<Tuple<Expression, Sequence<Statement>, SourceContext>> elseIfs, SourceContext finalContext, Sequence<Statement> finalElse)
        //{
        //    for (int i = elseIfs.Count - 1; i >= 0; i--)
        //    {
        //        Expression test = elseIfs[i].Item1;
        //        Sequence<Statement> body = elseIfs[i].Item2;
        //        SourceContext context = new SourceContext(elseIfs[i].Item3.Begin, finalContext.End);
        //        Statement statement = Statement.NewIf(test, body, finalElse, context);
        //        finalElse = Sequence.Create(statement);
        //    }
        //    return finalElse;
        //}

        //private Statement CreateForStatement(bool isAsync, Expression target, Expression iterator, Sequence<Statement> body, Sequence<Statement> orElse, SourceContext context)
        //{
        //    _validator.SetUsage(target, ExpressionUsage.Store);
        //    return Statement.NewFor(isAsync, target, iterator, body, orElse, context);
        //}

        //private Parameter CreateParameter(Identifier name, TypeExpression annotation, SourceContext context)
        //{
        //    if (name == null)
        //        throw new ArgumentNullException(nameof(name));
        //    _validator.CheckForbiddenName(name);
        //    return new Parameter(name, annotation, context);
        //}

        //private Statement CreateFunctionStatement(bool isAsync, Identifier name, ParameterList parameters, Sequence<Statement> body, Sequence<Expression> decorators, TypeExpression returns, SourceContext context)
        //{
        //    if (name == null)
        //        throw new ArgumentNullException(nameof(name));
        //    _validator.CheckForbiddenName(name);
        //    FunctionDeclaration decl = FunctionDeclaration.New(isAsync, name, parameters, body, decorators, returns, context);
        //    return Statement.NewFunction(decl);
        //}

        //private void CheckCallArgumentOrder(Tuple<Sequence<Expression>, Sequence<KeywordArgument>> args, SourceContext context, bool forStar)
        //{
        //    if (forStar)
        //    {
        //        foreach (KeywordArgument kw in args.Item2)
        //        {
        //            if (kw.Keyword == null)
        //            {
        //                SyntaxErrorException e = new SyntaxErrorException(SR.IterableUnpackingAfterKeywordUnpacking);
        //                e.AddLocation(kw.Context);
        //                e.AddLocation(context);
        //                throw e;
        //            }
        //        }
        //    }
        //    else
        //    {
        //        foreach (KeywordArgument kw in args.Item2)
        //        {
        //            if (kw.Keyword == null)
        //            {
        //                SyntaxErrorException e = new SyntaxErrorException(SR.PositionalAfterKeywordUnpacking);
        //                e.AddLocation(kw.Context);
        //                e.AddLocation(context);
        //                throw e;
        //            }
        //        }
        //        if (args.Item2.Count > 0)
        //        {
        //            SyntaxErrorException e = new SyntaxErrorException(SR.PositionalAfterKeyword);
        //            e.AddLocation(context);
        //            throw e;
        //        }
        //    }
        //}

        //private Expression CheckExpressionList(bool forceTuple, SourceContext context, Sequence<Expression> list, ExpressionUsage usage)
        //{
        //    if (!forceTuple && list.Count == 1)
        //        return list[0];
        //    else
        //        return Expression.NewTuple(list, usage, context);
        //}

        //private SequenceSlice CheckSubscriptList(Sequence<SequenceSlice> items, bool hasTrailingComma, SourceContext c)
        //{
        //    bool allSimple = true;
        //    foreach (SequenceSlice item in items)
        //        if (!item.IsIndexer)
        //            allSimple = false;
        //    if (allSimple)
        //    {
        //        if (items.Count > 1 || hasTrailingComma)
        //        {
        //            Sequence<Expression> list = Sequence.New(from n in items select n.AsIndexer.Value);
        //            Expression t = Expression.NewTuple(list, ExpressionUsage.Load, c);
        //            return SequenceSlice.NewIndexer(t, c);
        //        }
        //        return items[0];
        //    }
        //    if (items.Count > 1)
        //        return SequenceSlice.NewExtended(items, c);
        //    return items[0];
        //}

        //private void CheckExceptionHandlerList(Sequence<ExceptionHandler> handlers)
        //{
        //    for (int i = 0; i < handlers.Count - 1; i++)
        //    {
        //        if (handlers[i].Type == null)
        //        {
        //            SyntaxErrorException e = new SyntaxErrorException(SR.DefaultExceptionHandlerMustBeLast);
        //            e.AddLocation(handlers[i].Context);
        //            throw e;
        //        }
        //    }
        //}

        //private Tuple<Sequence<Parameter>, Sequence<Expression>> CheckPositionalDefaults(Tuple<Sequence<Parameter>, Sequence<Expression>> parameters)
        //{
        //    int first = -1;
        //    for (int i = 0; i < parameters.Item1.Count; i++)
        //    {
        //        if (parameters.Item2[i] != null)
        //        {
        //            first = i;
        //            break;
        //        }
        //    }
        //    if (first == -1)
        //        return Tuple.Create(parameters.Item1, parameters.Item2.Clear());
        //    else
        //    {
        //        for (int i = first + 1; i < parameters.Item1.Count; i++)
        //        {
        //            if (parameters.Item2[i] == null)
        //            {
        //                SyntaxErrorException e = new SyntaxErrorException(SR.NonDefaultArgFollowDefaultArg);
        //                e.AddLocation(parameters.Item1[first].Name.Context);
        //                e.AddLocation(parameters.Item1[i].Name.Context);
        //                throw e;
        //            }
        //        }
        //        return Tuple.Create(parameters.Item1, parameters.Item2.RemoveRangeAt(0, first));
        //    }
        //}

        //private bool CheckAnnAssignStatementTarget(Expression n, SourceContext context)
        //{
        //    if (n == null)
        //        throw new ArgumentNullException(nameof(n));
        //    _validator.SetUsageForAnnAssign(n);
        //    string str = context.GetSource();
        //    if (str.StartsWith("("))
        //        return false;
        //    return n.IsNameReference;
        //}

        //private void ErrorImportTrailingComma(SourceContext context)
        //{
        //    SyntaxErrorException e = new SyntaxErrorException(SR.TrailingCommaNotAllowedWithoutSurroundingParens);
        //    e.AddLocation(context);
        //    throw e;
        //}
        #endregion
    }
}
